Skip to content

next gen template initial version#1069

Open
jomen-adfa wants to merge 27 commits intostagefrom
feat/ADFA-2737-template-initial
Open

next gen template initial version#1069
jomen-adfa wants to merge 27 commits intostagefrom
feat/ADFA-2737-template-initial

Conversation

@jomen-adfa
Copy link
Contributor

initial next gen template

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Adds ZIP-based template support with Pebble rendering, dynamic discovery/loading of template archives from Environment.TEMPLATES_DIR, template API extensions (templateNameStr, thumbData, nullable language/SDKs), asset packaging/installers for a core template archive, and UI thumbnail/name updates with logging.

Changes

Cohort / File(s) Summary
ZIP Template Core
templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipJson.kt, .../ZipTemplateConstants.kt, .../ZipTemplateReader.kt, .../ZipRecipeExecutor.kt
Add JSON metadata models, constants, ZIP reader, and Pebble-based recipe executor to load and render templates from zip archives, handle thumbnails, parameters, and language-aware file skipping.
Core Template API
templates-api/src/main/java/com/itsaky/androidide/templates/template.kt, templates-api/src/main/java/com/itsaky/androidide/templates/base/base.kt
Extend Template/ProjectTemplate/Builder with templateNameStr and thumbData; make language/minSdk nullable; add baseZipProject() helper for zip-backed templates.
Template Provider
templates-impl/src/main/java/com/itsaky/androidide/templates/impl/TemplateProviderImpl.kt
Replace hardcoded templates with archive-driven loading from Environment.TEMPLATES_DIR, add reload flow, per-archive logging, error isolation, and getTemplate(id) accessor.
Builder & Module Changes
templates-api/src/main/java/com/itsaky/androidide/templates/base/ProjectTemplateBuilder.kt, .../modules/android/buildGradle.kt
Pass templateNameStr and thumbData when building templates; make Gradle generation null-safe for minSdk (minSdk?.api).
UI & Adapters
app/src/main/java/com/itsaky/androidide/adapters/TemplateListAdapter.kt, app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt
Switch display to templateNameStr, load thumbnails via Glide when available, add debug logging, and show language with null-safe fallback.
Assets & Environment
common/src/main/java/com/itsaky/androidide/utils/Environment.java, app/src/main/java/com/itsaky/androidide/assets/*, composite-builds/.../constants.kt, gradle/libs.versions.toml, templates-impl/build.gradle.kts, templates-api/build.gradle.kts
Add Environment.TEMPLATES_DIR; introduce template archive constants and assets (core.cgt, core.cgt.br); update installers to extract/install core archive; add Pebble and Gson deps and constants.

Sequence Diagram

sequenceDiagram
    participant Client
    participant TemplateProviderImpl
    participant ZipTemplateReader
    participant ZipFile
    participant ZipRecipeExecutor
    participant PebbleEngine
    participant FileSystem

    Client->>TemplateProviderImpl: getTemplates()
    TemplateProviderImpl->>FileSystem: list files in Environment.TEMPLATES_DIR
    loop per archive
        TemplateProviderImpl->>ZipTemplateReader: read(zipFile)
        ZipTemplateReader->>ZipFile: open archive & read templates.json
        loop per template ref
            ZipTemplateReader->>ZipFile: read template.json + thumb.png
            ZipTemplateReader->>TemplateProviderImpl: construct ProjectTemplate (baseZipProject)
        end
        ZipTemplateReader->>ZipFile: close
    end

    Client->>ZipRecipeExecutor: execute(executor) (when creating project)
    ZipRecipeExecutor->>ZipFile: open archive
    ZipRecipeExecutor->>PebbleEngine: render .peb templates with params
    loop per entry
        ZipRecipeExecutor->>ZipRecipeExecutor: shouldSkipFile(language)?
        alt peb template
            PebbleEngine-->>ZipRecipeExecutor: rendered content
        else raw file
            ZipRecipeExecutor-->>ZipRecipeExecutor: copy bytes
        end
        ZipRecipeExecutor->>FileSystem: write file to project dir
    end
    ZipRecipeExecutor->>ZipFile: close
    ZipRecipeExecutor->>Client: return ProjectTemplateRecipeResult
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • itsaky-adfa
  • jatezzz
  • Daniel-ADFA

Poem

🐰 I hopped through zips with whiskers bright,

Pebble sparks and thumbnails in sight.
I unwrap templates, stitch params with care,
Projects spring from archives, tidy and fair.
🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.12% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'next gen template initial version' directly describes the main change in the pull request.
Description check ✅ Passed The description 'initial next gen template' is related to the changeset and refers to the main feature being introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ADFA-2737-template-initial

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (6)
templates-impl/src/main/java/com/itsaky/androidide/templates/impl/TemplateProviderImpl.kt (2)

54-54: Remove commented-out code.

The commented line // val folder = File("/sdcard/Download/templates") should be removed before merging.

♻️ Cleanup
   private fun initializeTemplates() {
-    // val folder = File("/sdcard/Download/templates")
     val folder = TEMPLATES_DIR
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/TemplateProviderImpl.kt`
at line 54, Remove the leftover commented-out declaration in
TemplateProviderImpl.kt: delete the line "// val folder =
File("/sdcard/Download/templates")" (the unused commented variable `folder`) so
the class TemplateProviderImpl no longer contains stray commented code before
merging.

90-95: Remove commented-out coroutine code or implement async loading.

The commented-out CoroutineScope(Dispatchers.IO).launch suggests async loading was considered. Either implement it or remove the commented code. Note that initializeTemplates() currently runs synchronously, which could block if called on the main thread.

♻️ Cleanup commented code
   override fun reload() {
     release()
-  //  CoroutineScope(Dispatchers.IO).launch {
       initializeTemplates()
-  //  }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/TemplateProviderImpl.kt`
around lines 90 - 95, The reload() method in TemplateProviderImpl contains
commented-out coroutine code around initializeTemplates(), so either remove the
commented CoroutineScope(Dispatchers.IO).launch lines to clean up synchronous
behavior or actually perform async loading by running initializeTemplates()
inside a CoroutineScope(Dispatchers.IO).launch (or a passed-in scope), ensuring
you keep release() semantics, catch and log exceptions from
initializeTemplates(), and marshal any UI/thread-bound callbacks back to the
main dispatcher; refer to TemplateProviderImpl.reload(), initializeTemplates(),
CoroutineScope(Dispatchers.IO).launch and Dispatchers.IO when making the change.
templates-api/src/main/java/com/itsaky/androidide/templates/base/ProjectTemplateBuilder.kt (1)

194-204: Non-null assertions follow existing pattern but carry crash risk.

The !! operators on templateNameStr and thumbData are consistent with the existing pattern for templateName!!, thumb!!, widgets!!, and recipe!!. However, this means if a caller fails to set these properties before calling buildInternal(), a NullPointerException will be thrown at runtime.

Consider whether more graceful error handling (e.g., checkNotNull() with a descriptive message) would improve debuggability, consistent with how defModule is validated at line 56.

♻️ Optional improvement for better error messages
 override fun buildInternal(): ProjectTemplate =
 	ProjectTemplate(
 		modules,
-		templateName!!,
-		thumb!!,
+		checkNotNull(templateName) { "templateName must be set before building" },
+		checkNotNull(thumb) { "thumb must be set before building" },
 		tooltipTag,
-		widgets!!,
-		recipe!!,
-		templateNameStr!!,
-		thumbData!!
+		checkNotNull(widgets) { "widgets must be set before building" },
+		checkNotNull(recipe) { "recipe must be set before building" },
+		checkNotNull(templateNameStr) { "templateNameStr must be set before building" },
+		checkNotNull(thumbData) { "thumbData must be set before building" }
 	)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@templates-api/src/main/java/com/itsaky/androidide/templates/base/ProjectTemplateBuilder.kt`
around lines 194 - 204, Replace direct non-null assertions in
ProjectTemplateBuilder.buildInternal() with explicit checks using checkNotNull
(or requireNotNull) that include descriptive messages; specifically validate
templateNameStr and thumbData (and optionally templateName, thumb, widgets,
recipe) before constructing ProjectTemplate so failures produce clear error
messages similar to the defModule validation pattern, referencing
buildInternal(), ProjectTemplateBuilder, and the properties templateNameStr and
thumbData to locate where to add the checks.
app/src/main/java/com/itsaky/androidide/adapters/TemplateListAdapter.kt (1)

78-82: Remove or reduce verbose debug logging before merging.

The debug logs at lines 78-80 and 82 are useful during development but may be excessive for production. They're called for every item in the RecyclerView, potentially logging large amounts of data.

♻️ Suggested cleanup
-      log.debug("template: $template")
       templateName.text = template.templateNameStr
-      log.debug("text: ${template.templateNameStr} templateName.text: ${templateName.text}")
       if (template.thumbData != null) {
-        log.debug("thumbData is not null")
         Glide.with(templateIcon.context)
           .asBitmap()
           .load(template.thumbData)
           .into(templateIcon)
       } else {
         templateIcon.setImageResource(template.thumb)
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/adapters/TemplateListAdapter.kt`
around lines 78 - 82, In TemplateListAdapter (inside the RecyclerView binding
code, e.g., onBindViewHolder or bind method) remove or reduce the three per-item
debug statements (the log.debug calls that print "template: $template", "text:
${template.templateNameStr} ..." and "thumbData is not null"); either delete
them for production or wrap them behind a debug-level guard (e.g.,
log.isDebugEnabled) or replace with a single lightweight log that avoids
printing full objects, and leave a single conditional debug for thumbData if
absolutely needed.
templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipTemplateReader.kt (2)

60-64: Remove commented-out code before merging.

This commented block appears to be dead code. If the widget parsing logic is needed for future work, consider tracking it in an issue rather than leaving commented code in the codebase.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipTemplateReader.kt`
around lines 60 - 64, Remove the commented-out widget parsing block to clean up
dead code: delete the commented lines that define the widgets variable
alternatives (the lines referencing widgetsEntry, zip.getInputStream,
parseWidgetsFromJson(...) and the fallback metaJson.widgets.mapNotNull {
it.toWidget() }) so only the active widget-parsing implementation remains; if
this alternative logic must be preserved for future reference, create an issue
or add a brief note in the commit message instead of leaving commented code in
ZipTemplateReader.kt.

86-92: Prefer narrow exception handling over broad catch (e: Exception).

Using a broad catch (e: Exception) masks specific failure modes and can hide bugs. Per project learnings, catch only the specific exception types expected (e.g., IOException, JsonSyntaxException) and let unexpected exceptions propagate for fail-fast behavior during development.

♻️ Proposed narrower exception handling
-        } catch (e: Exception) {
+        } catch (e: IOException) {
+          log.error("Failed to load template at ${templateRef.path}", e)
+        } catch (e: com.google.gson.JsonSyntaxException) {
           log.error("Failed to load template at ${templateRef.path}", e)
         }
       }
-    } catch (e: Exception) {
+    } catch (e: IOException) {
+      log.error("Failed to read zip file $zipFile", e)
+    } catch (e: java.util.zip.ZipException) {
       log.error("Failed to read zip file $zipFile", e)
-      // return emptyList()
     }

Based on learnings: "prefer narrow exception handling that catches only the specific exception type reported in crashes (such as IllegalArgumentException) instead of a broad catch-all."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipTemplateReader.kt`
around lines 86 - 92, Replace the broad catch blocks in ZipTemplateReader with
narrow, specific exception handlers: for the inner block that logs "Failed to
load template at ${templateRef.path}" catch expected parsing/validation
exceptions (e.g., JsonSyntaxException, IllegalArgumentException) and IO-related
exceptions separately and log them; for the outer block that logs "Failed to
read zip file $zipFile" catch IOExceptions (and ZipException if applicable) only
and let other unexpected exceptions propagate. Update the catch clauses around
the code that reads/parses entries (references: ZipTemplateReader, the inner
template-loading try block and the outer zip-reading try block) to use multiple
specific catches rather than catch (e: Exception).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt`:
- Around line 139-142: Replace the hardcoded "unknown" fallback with a string
resource: add <string name="language_unknown">Unknown</string> to strings.xml
and update the assignment in TemplateDetailsFragment (where language is set from
result.data.language?.name) to use getString(R.string.language_unknown) (or
requireContext().getString(...)) as the default; this preserves i18n and keeps
the nullable-safe fallback intact.

In
`@composite-builds/build-deps-common/constants/src/main/java/org/adfa/constants/constants.kt`:
- Around line 81-82: The constant TEMPLATE_CORE_ARCHIVE is incorrectly
concatenated as "core$TEMPLATE_ARCHIVE_EXTENSION" which yields "corecgt" instead
of "core.cgt"; update the concatenation for TEMPLATE_CORE_ARCHIVE to include the
missing dot (e.g., "core.$TEMPLATE_ARCHIVE_EXTENSION" or equivalent) so it
matches File.extension behavior and produces "core.cgt".

In `@templates-api/src/main/java/com/itsaky/androidide/templates/template.kt`:
- Around line 344-348: The new requireNotNull(templateNameStr) in
Template<R>.build() causes templates constructed via
baseProject()/baseProjectImpl() (e.g., emptyActivityProject(),
noActivityProjectTemplate()) to throw because they never set templateNameStr;
either remove/relax the require in build() so templateNameStr is optional at
build time, or update all baseProject*/template builders to set templateNameStr
before calling build(); locate the check in build() (template.kt) and choose one
fix: (A) delete or replace requireNotNull(templateNameStr) with an optional
fallback/nullable handling, or (B) ensure baseProject(), baseProjectImpl(),
emptyActivityProject(), noActivityProjectTemplate() set templateNameStr (similar
to ZipTemplateReader which assigns this.templateNameStr = metaJson.name) prior
to build().

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/TemplateProviderImpl.kt`:
- Around line 76-78: Replace the broad catch (e: Throwable) {
e.printStackTrace() } in TemplateProviderImpl with a narrow catch for the
specific exception(s) observed in crashes (e.g., catch (e:
IllegalArgumentException)) and use the class SLF4J logger to record the error
(e.g., logger.error("Descriptive message about what failed", e)); if multiple
specific exceptions can occur, add separate catches for them rather than
catching Throwable or Exception.

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipJson.kt`:
- Around line 84-107: The toWidget() implementation currently calls
StringParameterBuilder.build() and BooleanParameterBuilder.build() without
providing required fields (name and for string default), causing NPEs; guard
these calls by validating WidgetJson.name (and WidgetJson.default for "string")
before constructing builders—if name is null/blank or string default is null,
log a warning and return null instead of building; update the "string" and
"boolean" branches in toWidget() to perform these checks prior to using
StringParameterBuilder and BooleanParameterBuilder so build() is never called
with missing required values.

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipRecipeExecutor.kt`:
- Around line 44-48: The ZipFile returned by zipProvider() (variable zip in
ZipRecipeExecutor.kt) is never closed; wrap the zipProvider() call so the
ZipFile is closed after use (e.g., use Kotlin's .use { zip -> ... } or a
try/finally that calls zip.close()), moving the early-return logic that returns
ProjectTemplateRecipeResultImpl(data) inside the resource block so the zip is
closed in all code paths before returning.
- Around line 93-98: Pebble template parsing/evaluation in the ZipRecipeExecutor
(calls to pebbleEngine.getTemplate(content) and template.evaluate(writer,
params) while processing entry.name) can throw PebbleException and currently
will abort ZIP processing; wrap the template retrieval and evaluation for
TEMPLATE_EXTENSION entries in a try/catch that catches PebbleException (and
optionally Exception) so a single bad template is logged via log.error including
entry.name and the exception, and then skip writing the outFile for that entry
(or write a safe fallback) and continue processing the rest of the ZIP; ensure
you still close any writers/streams (StringWriter) in finally or use Kotlin
use/try-with-resources patterns so resources are not leaked.
- Around line 89-103: The current code reads the entry content once into content
via zip.getInputStream(entry).bufferedReader().readText() but then re-opens
streams and creates a FileOutputStream even for template files
(TEMPLATE_EXTENSION) where outFile.writeText(...) is used instead of the opened
output, causing redundant IO and brief file locks; fix by branching on
entry.name.endsWith(TEMPLATE_EXTENSION) before opening the input/output streams:
for template files use the already-read content with
pebbleEngine.getTemplate(content) / template.evaluate(...) and write via
outFile.writeText(...); for non-template files skip reading into content and
instead open zip.getInputStream(entry).use { input ->
FileOutputStream(outFile).use { output -> input.copyTo(output) } } so
FileOutputStream is only created when actually used and streams are not
duplicated.

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipTemplateReader.kt`:
- Around line 29-31: ZipFile opened in ZipTemplateReader (the local variable
zip) is never closed; wrap ZipFile(zipFile) in a Kotlin use { } block or
otherwise ensure zip.close() is called (e.g., try-with-resources style) inside
the method that constructs it so the ZipFile is closed on all code paths and
avoids leaking file handles.

---

Nitpick comments:
In `@app/src/main/java/com/itsaky/androidide/adapters/TemplateListAdapter.kt`:
- Around line 78-82: In TemplateListAdapter (inside the RecyclerView binding
code, e.g., onBindViewHolder or bind method) remove or reduce the three per-item
debug statements (the log.debug calls that print "template: $template", "text:
${template.templateNameStr} ..." and "thumbData is not null"); either delete
them for production or wrap them behind a debug-level guard (e.g.,
log.isDebugEnabled) or replace with a single lightweight log that avoids
printing full objects, and leave a single conditional debug for thumbData if
absolutely needed.

In
`@templates-api/src/main/java/com/itsaky/androidide/templates/base/ProjectTemplateBuilder.kt`:
- Around line 194-204: Replace direct non-null assertions in
ProjectTemplateBuilder.buildInternal() with explicit checks using checkNotNull
(or requireNotNull) that include descriptive messages; specifically validate
templateNameStr and thumbData (and optionally templateName, thumb, widgets,
recipe) before constructing ProjectTemplate so failures produce clear error
messages similar to the defModule validation pattern, referencing
buildInternal(), ProjectTemplateBuilder, and the properties templateNameStr and
thumbData to locate where to add the checks.

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/TemplateProviderImpl.kt`:
- Line 54: Remove the leftover commented-out declaration in
TemplateProviderImpl.kt: delete the line "// val folder =
File("/sdcard/Download/templates")" (the unused commented variable `folder`) so
the class TemplateProviderImpl no longer contains stray commented code before
merging.
- Around line 90-95: The reload() method in TemplateProviderImpl contains
commented-out coroutine code around initializeTemplates(), so either remove the
commented CoroutineScope(Dispatchers.IO).launch lines to clean up synchronous
behavior or actually perform async loading by running initializeTemplates()
inside a CoroutineScope(Dispatchers.IO).launch (or a passed-in scope), ensuring
you keep release() semantics, catch and log exceptions from
initializeTemplates(), and marshal any UI/thread-bound callbacks back to the
main dispatcher; refer to TemplateProviderImpl.reload(), initializeTemplates(),
CoroutineScope(Dispatchers.IO).launch and Dispatchers.IO when making the change.

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipTemplateReader.kt`:
- Around line 60-64: Remove the commented-out widget parsing block to clean up
dead code: delete the commented lines that define the widgets variable
alternatives (the lines referencing widgetsEntry, zip.getInputStream,
parseWidgetsFromJson(...) and the fallback metaJson.widgets.mapNotNull {
it.toWidget() }) so only the active widget-parsing implementation remains; if
this alternative logic must be preserved for future reference, create an issue
or add a brief note in the commit message instead of leaving commented code in
ZipTemplateReader.kt.
- Around line 86-92: Replace the broad catch blocks in ZipTemplateReader with
narrow, specific exception handlers: for the inner block that logs "Failed to
load template at ${templateRef.path}" catch expected parsing/validation
exceptions (e.g., JsonSyntaxException, IllegalArgumentException) and IO-related
exceptions separately and log them; for the outer block that logs "Failed to
read zip file $zipFile" catch IOExceptions (and ZipException if applicable) only
and let other unexpected exceptions propagate. Update the catch clauses around
the code that reads/parses entries (references: ZipTemplateReader, the inner
template-loading try block and the outer zip-reading try block) to use multiple
specific catches rather than catch (e: Exception).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1c8437c1-522d-4e39-b8ee-cf69e7019cb9

📥 Commits

Reviewing files that changed from the base of the PR and between 29d26af and b8d63dc.

📒 Files selected for processing (16)
  • app/src/main/java/com/itsaky/androidide/adapters/TemplateListAdapter.kt
  • app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt
  • app/src/main/java/com/itsaky/androidide/fragments/TemplateListFragment.kt
  • common/src/main/java/com/itsaky/androidide/utils/Environment.java
  • composite-builds/build-deps-common/constants/src/main/java/org/adfa/constants/constants.kt
  • gradle/libs.versions.toml
  • templates-api/build.gradle.kts
  • templates-api/src/main/java/com/itsaky/androidide/templates/base/ProjectTemplateBuilder.kt
  • templates-api/src/main/java/com/itsaky/androidide/templates/base/base.kt
  • templates-api/src/main/java/com/itsaky/androidide/templates/template.kt
  • templates-impl/build.gradle.kts
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/TemplateProviderImpl.kt
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipJson.kt
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipRecipeExecutor.kt
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipTemplateConstants.kt
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipTemplateReader.kt

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@templates-api/src/main/java/com/itsaky/androidide/templates/base/modules/android/buildGradle.kt`:
- Line 100: The template currently interpolates data.versions.minSdk?.api
directly (e.g. the string "minSdk = ${data.versions.minSdk?.api}") which can
emit "minSdk = null"; change the template generation so the entire "minSdk =
..." line is only emitted when data.versions.minSdk?.api is non-null (use a
conditional/let to omit the line if null) and apply this fix to all four
occurrences in buildGradle.kt (the KTS and Groovy template generators and their
NDK variants where data.versions.minSdk?.api is used).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 19f0d752-60b8-48c1-a5e7-bdc05c91538b

📥 Commits

Reviewing files that changed from the base of the PR and between b8d63dc and 4da6ceb.

📒 Files selected for processing (1)
  • templates-api/src/main/java/com/itsaky/androidide/templates/base/modules/android/buildGradle.kt

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt`:
- Around line 83-91: The new TEMPLATE_CORE_ARCHIVE case copies the bundled
archive but BundledAssetsInstaller.expectedSize() still returns 0 for
TEMPLATE_CORE_ARCHIVE, causing progress misreporting; update
BundledAssetsInstaller.expectedSize() to return the real expected size for
TEMPLATE_CORE_ARCHIVE (matching how other assets are handled) by computing or
returning the compressed or uncompressed byte length used by the installer so
AssetsInstallationHelper.totalSize includes TEMPLATE_CORE_ARCHIVE; locate the
expectedSize() method and add a branch for TEMPLATE_CORE_ARCHIVE (referencing
TEMPLATE_CORE_ARCHIVE constant) that returns the correct long value (or derives
it from the same source ToolsManager.getCommonAsset / asset metadata) to ensure
progress reflects the actual copy work.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6155d922-1073-4c2e-be78-7f8dfd09e922

📥 Commits

Reviewing files that changed from the base of the PR and between 4da6ceb and ac5a392.

📒 Files selected for processing (5)
  • app/build.gradle.kts
  • app/src/main/java/com/itsaky/androidide/assets/AssetsInstallationHelper.kt
  • app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt
  • app/src/main/java/com/itsaky/androidide/assets/SplitAssetsInstaller.kt
  • composite-builds/build-deps-common/constants/src/main/java/org/adfa/constants/constants.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • composite-builds/build-deps-common/constants/src/main/java/org/adfa/constants/constants.kt

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipRecipeExecutor.kt (1)

94-108: ⚠️ Potential issue | 🟠 Major

Remove redundant stream work and ensure every opened stream is closed.

Line 94 reads the whole entry (as text) before branching, and that stream is not managed with use. Also, FileOutputStream is opened for template entries where it is never used.

Proposed fix
-          val content = zip.getInputStream(entry).bufferedReader().readText()
-
-          zip.getInputStream(entry).use { input ->
-            FileOutputStream(outFile).use { output ->
-              if (entry.name.endsWith(TEMPLATE_EXTENSION)) {
-                log.debug("template processing ${entry.name}")
-                val template = pebbleEngine.getTemplate(content)
-                val writer = StringWriter()
-                template.evaluate(writer, params)
-                outFile.writeText(writer.toString(), Charsets.UTF_8)
-              } else {
-                input.copyTo(output)
-              }
-            }
-          }
+          if (entry.name.endsWith(TEMPLATE_EXTENSION)) {
+            log.debug("template processing ${entry.name}")
+            val content = zip.getInputStream(entry).bufferedReader().use { it.readText() }
+            val template = pebbleEngine.getTemplate(content)
+            val writer = StringWriter()
+            template.evaluate(writer, params)
+            outFile.writeText(writer.toString(), Charsets.UTF_8)
+          } else {
+            zip.getInputStream(entry).use { input ->
+              FileOutputStream(outFile).use { output ->
+                input.copyTo(output)
+              }
+            }
+          }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipRecipeExecutor.kt`
around lines 94 - 108, The code reads the entry into val content via
zip.getInputStream(entry).bufferedReader().readText() without using use(), then
later opens zip.getInputStream(entry) again and always creates a
FileOutputStream(outFile) even for template entries; fix ZipRecipeExecutor by
removing the initial readText call, only open the entry InputStream inside a use
block when copying binary entries, and for TEMPLATE_EXTENSION entries read the
entry via a buffered reader in a use block (or obtain the entry content via a
single use-managed stream) and write the rendered template with
outFile.writeText (creating FileOutputStream only when actually copying binary
data), ensuring every InputStream/FileOutputStream/Reader/Writer is wrapped with
use so all streams are closed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipRecipeExecutor.kt`:
- Around line 85-93: The code constructs output paths from ZipEntry names
(relativePath, outFile) without validating them, allowing zip-slip path
traversal; fix by resolving the target path against projectDir and verifying the
canonical path is inside projectDir before creating/writing: after computing
outFile (from relativePath.removeSuffix(TEMPLATE_EXTENSION)), call canonicalFile
on both projectDir and outFile and ensure outFile.canonicalPath
startsWith(projectDir.canonicalPath + File.separator) (or equals
projectDir.canonicalPath) and if not, skip or throw; only then create parent
dirs (outFile.parentFile?.mkdirs()) and write the file, keeping the existing
handling for directories (entry.isDirectory) intact and apply the same
validation for directory entries.

---

Duplicate comments:
In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipRecipeExecutor.kt`:
- Around line 94-108: The code reads the entry into val content via
zip.getInputStream(entry).bufferedReader().readText() without using use(), then
later opens zip.getInputStream(entry) again and always creates a
FileOutputStream(outFile) even for template entries; fix ZipRecipeExecutor by
removing the initial readText call, only open the entry InputStream inside a use
block when copying binary entries, and for TEMPLATE_EXTENSION entries read the
entry via a buffered reader in a use block (or obtain the entry content via a
single use-managed stream) and write the rendered template with
outFile.writeText (creating FileOutputStream only when actually copying binary
data), ensuring every InputStream/FileOutputStream/Reader/Writer is wrapped with
use so all streams are closed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4713170a-32e7-4bb4-af9f-466c0a3bb638

📥 Commits

Reviewing files that changed from the base of the PR and between ac5a392 and eb1715b.

📒 Files selected for processing (4)
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/TemplateProviderImpl.kt
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipJson.kt
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipRecipeExecutor.kt
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipTemplateReader.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipTemplateReader.kt

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipRecipeExecutor.kt (1)

150-154: Consider making helper methods private to reduce API surface.

These utilities appear internal to ZipRecipeExecutor and don’t need to be publicly exposed.

Also applies to: 156-159, 226-234

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipRecipeExecutor.kt`
around lines 150 - 154, Make the utility helpers non-public: change
safeLanguageName and safeMinSdkApi to private functions (e.g., private fun
safeLanguageName(...) and private fun safeMinSdkApi(...)) and likewise change
the other helper methods in the referenced ranges (startLine 156-159 and
226-234) to private; ensure you update any internal references within
ZipRecipeExecutor to match the new visibility and if any external code depends
on them, move those call sites into the class or provide a controlled public
facade instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipRecipeExecutor.kt`:
- Around line 43-48: Check data.projectDir.exists() before calling zipProvider()
so archive IO is not touched when we can short-circuit; move the projectDir
existence check to occur prior to invoking zipProvider() in ZipRecipeExecutor
(returning ProjectTemplateRecipeResultImpl(data) when it exists) to avoid
opening the ZIP unnecessarily.

In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipTemplateReader.kt`:
- Around line 101-103: In ZipTemplateReader, replace the broad catch blocks that
currently catch Exception with specific exception types: for the inner catch
that logs "Failed to load template at ${templateRef.path}" catch
java.io.IOException and com.google.gson.JsonParseException only (log the error
as before); for the outer catch around the ZIP/index opening logic catch
java.util.zip.ZipException, java.io.IOException and
com.google.gson.JsonParseException only; remove the generic catch so unexpected
exceptions propagate (and add the necessary imports).

---

Nitpick comments:
In
`@templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipRecipeExecutor.kt`:
- Around line 150-154: Make the utility helpers non-public: change
safeLanguageName and safeMinSdkApi to private functions (e.g., private fun
safeLanguageName(...) and private fun safeMinSdkApi(...)) and likewise change
the other helper methods in the referenced ranges (startLine 156-159 and
226-234) to private; ensure you update any internal references within
ZipRecipeExecutor to match the new visibility and if any external code depends
on them, move those call sites into the class or provide a controlled public
facade instead.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5d5e865c-daee-4476-9acb-56fe7bdc0781

📥 Commits

Reviewing files that changed from the base of the PR and between eb1715b and d7da774.

📒 Files selected for processing (4)
  • app/build.gradle.kts
  • app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipRecipeExecutor.kt
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/zip/ZipTemplateReader.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/build.gradle.kts
  • app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt

@jomen-adfa jomen-adfa force-pushed the feat/ADFA-2737-template-initial branch from 07c511d to 001241e Compare March 19, 2026 13:22
Copy link
Contributor

@itsaky-adfa itsaky-adfa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also run Spotless on this branch to format all files appropriately.

defaultConfig {
applicationId = "${data.packageName}"
minSdk = ${data.versions.minSdk.api}
minSdk = ${data.versions.minSdk?.api}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same non-optional input issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new template code is currently co-existing with the legacy template code and these changes were put into the legacy code to defer removal of the legacy template to after the new template code has stabilized. Had to make minSdk optional since there are cases in some templates now, like plugin, and in the future that don't seem to use this in the templates.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the minSdk is unused, that's fine. Templates can choose to ignore it if required, but I think it should never be undefined.

Although, I wonder why plugin templates don't have minSdk defined (@Daniel-ADFA?). I mean, what happens if a plugin uses APIs from newer Android versions, but the user tries to install that plugin on an older Android version? Wouldn't the plugin crash due to the API differences?

defaultConfig {
applicationId = "${data.packageName}"
minSdk = ${data.versions.minSdk.api}
minSdk = ${data.versions.minSdk?.api}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same non-optional input issue.


defaultConfig {
applicationId = "${data.packageName}"
minSdk = ${data.versions.minSdk.api}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same non-optional input issue.

@jomen-adfa jomen-adfa requested a review from itsaky-adfa March 20, 2026 13:38
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/java/com/itsaky/androidide/assets/AssetsInstallationHelper.kt`:
- Around line 117-118: The progress size for TEMPLATE_CORE_ARCHIVE is
inconsistent between BundledAssetsInstaller and SplitAssetsInstaller; unify by
computing and using the same expected size source for both installers (e.g.,
derive the decompressed archive size once and expose a shared helper or
constant) and update both installers to call that helper instead of hardcoding
different values; specifically change BundledAssetsInstaller and
SplitAssetsInstaller to reference a single function/constant (e.g.,
getExpectedSize(TEMPLATE_CORE_ARCHIVE) or EXPECTED_SIZES[TEMPLATE_CORE_ARCHIVE])
so expectedSize(TEMPLATE_CORE_ARCHIVE) returns the same value across both paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f7b6a3e5-8435-4dc6-9b68-b215b6a61448

📥 Commits

Reviewing files that changed from the base of the PR and between eb1715b and 60e9aaf.

📒 Files selected for processing (9)
  • app/build.gradle.kts
  • app/src/main/java/com/itsaky/androidide/adapters/TemplateListAdapter.kt
  • app/src/main/java/com/itsaky/androidide/assets/AssetsInstallationHelper.kt
  • app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt
  • app/src/main/java/com/itsaky/androidide/assets/SplitAssetsInstaller.kt
  • app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt
  • common/src/main/java/com/itsaky/androidide/utils/Environment.java
  • composite-builds/build-deps-common/constants/src/main/java/org/adfa/constants/constants.kt
  • gradle/libs.versions.toml
✅ Files skipped from review due to trivial changes (1)
  • gradle/libs.versions.toml
🚧 Files skipped from review as they are similar to previous changes (5)
  • common/src/main/java/com/itsaky/androidide/utils/Environment.java
  • app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt
  • composite-builds/build-deps-common/constants/src/main/java/org/adfa/constants/constants.kt
  • app/src/main/java/com/itsaky/androidide/assets/SplitAssetsInstaller.kt
  • app/src/main/java/com/itsaky/androidide/adapters/TemplateListAdapter.kt

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt (1)

139-140: ⚠️ Potential issue | 🟡 Minor

Replace hardcoded fallback text with a localized resource.

Line 140 still uses "unknown", which bypasses i18n.

💡 Proposed fix
-                        language = result.data.language?.name ?: "unknown"
+                        language = result.data.language?.name ?: getString(R.string.language_unknown)
<string name="language_unknown">Unknown</string>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt`
around lines 139 - 140, The code in TemplateDetailsFragment sets language =
result.data.language?.name ?: "unknown" which hardcodes fallback text; replace
the literal "unknown" with a localized string resource (e.g., use
context.getString(R.string.language_unknown) or requireContext().getString(...))
and add the corresponding <string name="language_unknown">Unknown</string> to
strings.xml so the fallback is localizable; update the assignment that uses
template.templateNameStr and result.data.language?.name to use the localized
resource when the language is null.
🧹 Nitpick comments (1)
app/src/main/java/com/itsaky/androidide/assets/SplitAssetsInstaller.kt (1)

76-81: Create the templates directory defensively before writing core.cgt.

This avoids a potential FileNotFoundException if directory initialization order changes.

Suggested patch
                             TEMPLATE_CORE_ARCHIVE -> {
-                                val coreCgt = Environment.TEMPLATES_DIR.resolve(TEMPLATE_CORE_ARCHIVE)
+                                Files.createDirectories(Environment.TEMPLATES_DIR.toPath())
+                                val coreCgt = Environment.TEMPLATES_DIR.resolve(TEMPLATE_CORE_ARCHIVE)
                                 coreCgt.outputStream().use { output ->
                                     zipInput.copyTo(output)
                                 }
                             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/assets/SplitAssetsInstaller.kt`
around lines 76 - 81, The code writes TEMPLATE_CORE_ARCHIVE into
Environment.TEMPLATES_DIR without ensuring that directory exists, which can
throw FileNotFoundException; before resolving TEMPLATE_CORE_ARCHIVE and opening
coreCgt.outputStream() in the SplitAssetsInstaller.kt block handling
TEMPLATE_CORE_ARCHIVE, create the directory defensively (e.g., call
mkdirs()/createDirectories on Environment.TEMPLATES_DIR) so the parent folder
exists before writing the file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt`:
- Around line 139-140: The code in TemplateDetailsFragment sets language =
result.data.language?.name ?: "unknown" which hardcodes fallback text; replace
the literal "unknown" with a localized string resource (e.g., use
context.getString(R.string.language_unknown) or requireContext().getString(...))
and add the corresponding <string name="language_unknown">Unknown</string> to
strings.xml so the fallback is localizable; update the assignment that uses
template.templateNameStr and result.data.language?.name to use the localized
resource when the language is null.

---

Nitpick comments:
In `@app/src/main/java/com/itsaky/androidide/assets/SplitAssetsInstaller.kt`:
- Around line 76-81: The code writes TEMPLATE_CORE_ARCHIVE into
Environment.TEMPLATES_DIR without ensuring that directory exists, which can
throw FileNotFoundException; before resolving TEMPLATE_CORE_ARCHIVE and opening
coreCgt.outputStream() in the SplitAssetsInstaller.kt block handling
TEMPLATE_CORE_ARCHIVE, create the directory defensively (e.g., call
mkdirs()/createDirectories on Environment.TEMPLATES_DIR) so the parent folder
exists before writing the file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 91e2711f-f38c-493c-831b-3e725aced278

📥 Commits

Reviewing files that changed from the base of the PR and between eb1715b and 60e9aaf.

📒 Files selected for processing (9)
  • app/build.gradle.kts
  • app/src/main/java/com/itsaky/androidide/adapters/TemplateListAdapter.kt
  • app/src/main/java/com/itsaky/androidide/assets/AssetsInstallationHelper.kt
  • app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt
  • app/src/main/java/com/itsaky/androidide/assets/SplitAssetsInstaller.kt
  • app/src/main/java/com/itsaky/androidide/fragments/TemplateDetailsFragment.kt
  • common/src/main/java/com/itsaky/androidide/utils/Environment.java
  • composite-builds/build-deps-common/constants/src/main/java/org/adfa/constants/constants.kt
  • gradle/libs.versions.toml
🚧 Files skipped from review as they are similar to previous changes (5)
  • common/src/main/java/com/itsaky/androidide/utils/Environment.java
  • composite-builds/build-deps-common/constants/src/main/java/org/adfa/constants/constants.kt
  • gradle/libs.versions.toml
  • app/src/main/java/com/itsaky/androidide/adapters/TemplateListAdapter.kt
  • app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt

@davidschachterADFA
Copy link
Collaborator

I'm not qualified to renew the code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants